<template>
	<div>
		<el-dialog v-dialogDrag :title="title" top="5vh" :close-on-click-modal="false" :close-on-press-escape="false"
			:visible.sync="showRoom" width="50%" height="70%" :before-close="onQuit">
			<div class="rtc-private-video">
				<div v-show="isVideo" class="rtc-video-box">
					<div class="rtc-video-friend" v-loading="!isChating"  element-loading-text="等待对方接听..."
						element-loading-background="rgba(0, 0, 0, 0.3)" >
						<head-image class="friend-head-image" :id="friend.id" :size="80" :name="friend.nickName"
							:url="friend.headImage">
						</head-image>
						<video ref="remoteVideo" autoplay=""></video>
					</div>
					<div class="rtc-video-mine">
						<video ref="localVideo" autoplay=""></video>
					</div>
				</div>
				<div v-show="!isVideo" class="rtc-voice-box" v-loading="!isChating" element-loading-text="等待对方接听..."
					element-loading-background="rgba(0, 0, 0, 0.3)">
					<head-image class="friend-head-image" :id="friend.id" :size="200" :name="friend.nickName"
						:url="friend.headImage">
						<div class="rtc-voice-name">{{friend.nickName}}</div>
					</head-image>
				</div>
				<div class="rtc-control-bar">
					<div title="取消" class="icon iconfont icon-phone-reject reject"
						style="color: red;" @click="onQuit()"></div>
				</div>
			</div>
		</el-dialog>
		<rtc-private-acceptor v-if="!isHost&&isWaiting" ref="acceptor" :friend="friend" :mode="mode" @accept="onAccept"
			@reject="onReject"></rtc-private-acceptor>
	</div>
</template>

<script>
	import HeadImage from '../common/HeadImage.vue';
	import RtcPrivateAcceptor from './RtcPrivateAcceptor.vue';
	import ImWebRtc from '@/api/webrtc';
	import ImCamera from '@/api/camera';
	import RtcPrivateApi from '@/api/rtcPrivateApi'

	export default {
		name: 'rtcPrivateVideo',
		components: {
			HeadImage,
			RtcPrivateAcceptor
		},
		data() {
			return {
				camera: new ImCamera(), // 摄像头和麦克风
				webrtc: new ImWebRtc(), // webrtc相关
				API: new RtcPrivateApi(), // API
				audio: new Audio(), // 呼叫音频
				showRoom: false,
				friend: {},
				isHost: false, // 是否发起人
				state: "CLOSE", // CLOSE:关闭  WAITING:等待呼叫或接听 CHATING:聊天中  ERROR:出现异常
				mode: 'video', // 模式 video:视频聊 voice:语音聊天
				localStream: null, // 本地视频流
				remoteStream: null, // 对方视频流
				videoTime: 0,
				videoTimer: null,
				heartbeatTimer: null,
				candidates: [],
			}
		},
		methods: {
			open(rtcInfo) {
				this.showRoom = true;
				this.mode = rtcInfo.mode;
				this.isHost = rtcInfo.isHost;
				this.friend = rtcInfo.friend;
				if (this.isHost) {
					this.onCall();
				}
			},
			initAudio() {
				let url = require(`@/assets/audio/call.wav`);
				this.audio.src = url;
				this.audio.loop = true;
			},
			initRtc() {
				this.webrtc.init(this.configuration)
				this.webrtc.setupPeerConnection((stream) => {
					this.$refs.remoteVideo.srcObject = stream;
					this.remoteStream = stream;
				})
				// 监听候选信息
				this.webrtc.onIcecandidate((candidate) => {
					if (this.state == "CHATING") {
						// 连接已就绪,直接发送
						this.API.sendCandidate(this.friend.id, candidate);
					} else {
						// 连接未就绪,缓存起来，连接后再发送
						this.candidates.push(candidate)
					}
				})
				// 监听连接成功状态
				this.webrtc.onStateChange((state) => {
					if (state == "connected") {
						console.log("webrtc连接成功")
					} else if (state == "disconnected") {
						console.log("webrtc连接断开")
					}
				})
			},
			onCall() {
				if (!this.checkDevEnable()) {
					this.close();
				}
				// 初始化webrtc
				this.initRtc();
				// 启动心跳
				this.startHeartBeat();
				// 打开摄像头
				this.openStream().then(() => {
					this.webrtc.setStream(this.localStream);
					this.webrtc.createOffer().then((offer) => {
						// 发起呼叫
						this.API.call(this.friend.id, this.mode, offer).then(() => {
							// 直接进入聊天状态
							this.state = "WAITING";
							// 播放呼叫铃声
							this.audio.play();
						}).catch(()=>{
							this.close();
						})
					})
				}).catch(()=>{
					// 呼叫方必须能打开摄像头，否则无法正常建立连接
					this.close();
				})
			},
			onAccept() {
				if (!this.checkDevEnable()) {
					this.API.failed(this.friend.id, "对方设备不支持通话")
					this.close();
					return;
				}
				// 进入房间
				this.showRoom = true;
				this.state = "CHATING";
				// 停止呼叫铃声
				this.audio.pause();
				// 初始化webrtc
				this.initRtc();
				// 打开摄像头
				this.openStream().finally(() => {
					this.webrtc.setStream(this.localStream);
					this.webrtc.createAnswer(this.offer).then((answer) => {
						this.API.accept(this.friend.id, answer);
						// 记录时长
						this.startChatTime();
						// 清理定时器
						this.waitTimer && clearTimeout(this.waitTimer);
					})
				})
			},
			onReject() {
				console.log("onReject")
				// 退出通话
				this.API.reject(this.friend.id);
				// 退出
				this.close();
			},
			onHandup() {
				this.API.handup(this.friend.id)
				this.$message.success("您已挂断,通话结束")
				this.close();
			},
			onCancel() {
				this.API.cancel(this.friend.id)
				this.$message.success("已取消呼叫,通话结束")
				this.close();
			},
			onRTCMessage(msg) {
				// 除了发起通话，如果在关闭状态就无需处理
				if (msg.type != this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE &&
					msg.type != this.$enums.MESSAGE_TYPE.RTC_CALL_VIDEO &&
					this.isClose) {
					return;
				}
				// RTC信令处理
				switch (msg.type) {
					case this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE:
						this.onRTCCall(msg, 'voice')
						break;
					case this.$enums.MESSAGE_TYPE.RTC_CALL_VIDEO:
						this.onRTCCall(msg, 'video')
						break;
					case this.$enums.MESSAGE_TYPE.RTC_ACCEPT:
						this.onRTCAccept(msg)
						break;
					case this.$enums.MESSAGE_TYPE.RTC_REJECT:
						this.onRTCReject(msg)
						break;
					case this.$enums.MESSAGE_TYPE.RTC_CANCEL:
						this.onRTCCancel(msg)
						break;
					case this.$enums.MESSAGE_TYPE.RTC_FAILED:
						this.onRTCFailed(msg)
						break;
					case this.$enums.MESSAGE_TYPE.RTC_HANDUP:
						this.onRTCHandup(msg)
						break;
					case this.$enums.MESSAGE_TYPE.RTC_CANDIDATE:
						this.onRTCCandidate(msg)
						break;
				}
			},
			onRTCCall(msg, mode) {
				this.offer = JSON.parse(msg.content);
				this.isHost = false;
				this.mode = mode;
				this.$http({
					url: `/friend/find/${msg.sendId}`,
					method: 'get'
				}).then((friend) => {
					this.friend = friend;
					this.state = "WAITING";
					this.audio.play();
					this.startHeartBeat();
					// 30s未接听自动挂掉
					this.waitTimer = setTimeout(() => {
						this.API.failed(this.friend.id,"对方无应答");
						this.$message.error("您未接听");
						this.close();
					}, 30000)
				})
			},
			onRTCAccept(msg) {
				if (msg.selfSend) {
					// 在其他设备接听
					this.$message.success("已在其他设备接听");
					this.close();
				} else {
					// 对方接受了的通话
					let offer = JSON.parse(msg.content);
					this.webrtc.setRemoteDescription(offer);
					// 状态为聊天中
					this.state = 'CHATING'
					// 停止播放语音
					this.audio.pause();
					// 发送candidate
					this.candidates.forEach((candidate) => {
						this.API.sendCandidate(this.friend.id, candidate);
					})
					// 开始计时
					this.startChatTime();
				}
			},
			onRTCReject(msg) {
				if (msg.selfSend) {
					this.$message.success("已在其他设备拒绝");
					this.close();
				} else {
					this.$message.error("对方拒绝了您的通话请求");
					this.close();
				}
			},
			onRTCFailed(msg) {
				// 呼叫失败
				this.$message.error(msg.content)
				this.close();
			},
			onRTCCancel() {
				// 对方取消通话
				this.$message.success("对方取消了呼叫");
				this.close();
			},
			onRTCHandup() {
				// 对方挂断
				this.$message.success("对方已挂断");
				this.close();
			},
			onRTCCandidate(msg) {
				let candidate = JSON.parse(msg.content);
				this.webrtc.addIceCandidate(candidate);
			},

			openStream() {
				return new Promise((resolve, reject) => {
					if (this.isVideo) {
						// 打开摄像头+麦克风
						this.camera.openVideo().then((stream) => {
							this.localStream = stream;
							this.$nextTick(() => {
								this.$refs.localVideo.srcObject = stream;
								this.$refs.localVideo.muted = true;
							})
							resolve(stream);
						}).catch((e) => {
							this.$message.error("打开摄像头失败")
							console.log("本摄像头打开失败:" + e.message)
							reject(e);
						})
					} else {
						// 打开麦克风
						this.camera.openAudio().then((stream) => {
							this.localStream = stream;
							this.$refs.localVideo.srcObject = stream;
							resolve(stream);
						}).catch((e) => {
							this.$message.error("打开麦克风失败")
							console.log("打开麦克风失败:" + e.message)
							reject(e);
						})
					}
				})
			},
			startChatTime() {
				this.videoTime = 0;
				this.videoTimer && clearInterval(this.videoTimer);
				this.videoTimer = setInterval(() => {
					this.videoTime++;
				}, 1000)
			},
			checkDevEnable() {
				// 检测摄像头
				if (!this.camera.isEnable()) {
					this.message.error("访问摄像头失败");
					return false;
				}
				// 检测webrtc
				if (!this.webrtc.isEnable()) {
					this.message.error("初始化RTC失败，原因可能是: 1.服务器缺少ssl证书 2.您的设备不支持WebRTC");
					return false;
				}
				return true;
			},
			startHeartBeat() {
				// 每15s推送一次心跳
				this.heartbeatTimer && clearInterval(this.heartbeatTimer);
				this.heartbeatTimer = setInterval(() => {
					this.API.heartbeat(this.friend.id);
				}, 15000)
			},
			close() {
				this.showRoom = false;
				this.camera.close();
				this.webrtc.close();
				this.audio.pause();
				this.videoTime = 0;
				this.videoTimer && clearInterval(this.videoTimer);
				this.heartbeatTimer && clearInterval(this.heartbeatTimer);
				this.waitTimer && clearTimeout(this.waitTimer);
				this.videoTimer = null;
				this.heartbeatTimer = null;
				this.waitTimer = null;
				this.state = 'CLOSE';
				this.candidates = [];
			},
			onQuit() {
				if (this.isChating) {
					this.onHandup()
				} else if (this.isWaiting) {
					this.onCancel();
				} else {
					this.close();
				}
			}
		},
		computed: {
			title() {
				let strTitle = `${this.modeText}通话-${this.friend.nickName}`;
				if (this.isChating) {
					strTitle += `(${this.currentTime})`;
				} else if (this.isWaiting) {
					strTitle += `(呼叫中)`;
				}
				return strTitle;
			},
			currentTime() {
				let min = Math.floor(this.videoTime / 60);
				let sec = this.videoTime % 60;
				let strTime = min < 10 ? "0" : "";
				strTime += min;
				strTime += ":"
				strTime += sec < 10 ? "0" : "";
				strTime += sec;
				return strTime;
			},
			configuration() {
				const iceServers = this.$store.state.configStore.webrtc.iceServers;
				return {
					iceServers: iceServers
				}
			},
			isVideo() {
				return this.mode == "video"
			},
			modeText() {
				return this.isVideo ? "视频" : "语音";
			},
			isChating() {
				return this.state == "CHATING";
			},
			isWaiting() {
				return this.state == "WAITING";
			},
			isClose() {
				return this.state == "CLOSE";
			}
		},
		mounted() {
			// 初始化音频文件
			this.initAudio();
		},
		created() {
			// 监听页面刷新事件
			window.addEventListener('beforeunload', () => {
				this.onQuit();
			});
		},
		beforeUnmount() {
			this.onQuit();
		}
	}
</script>

<style lang="scss">
	.rtc-private-video {
		position: relative;

		.el-loading-text {
			color: white !important;
			font-size: 16px !important;
		}

		.path {
			stroke: white !important;
		}

		.rtc-video-box {
			position: relative;
			border: #4880b9 solid 1px;
			background-color: #eeeeee;

			.rtc-video-friend {
				height: 70vh;

				.friend-head-image {
					position: absolute;
				}

				video {
					width: 100%;
					height: 100%;
					object-fit: cover;
					transform: rotateY(180deg);
				}
			}

			.rtc-video-mine {
				position: absolute;
				z-index: 99999;
				width: 25vh;
				right: 0;
				bottom: 0;
				box-shadow: 0px 0px 5px #ccc;
				background-color: #cccccc;

				video {
					width: 100%;
					object-fit: cover;
					transform: rotateY(180deg);
				}
			}
		}

		.rtc-voice-box {
			position: relative;
			display: flex;
			justify-content: center;
			border: #4880b9 solid 1px;
			width: 100%;
			height: 50vh;
			padding-top: 10vh;
			background-color: aliceblue;

			.rtc-voice-name {
				text-align: center;
				font-size: 22px;
				font-weight: 600;
			}
		}

		.rtc-control-bar {
			display: flex;
			justify-content: space-around;
			padding: 10px;

			.icon {
				font-size: 50px;
				cursor: pointer;
			}
		}
	}
</style>